#include "usart.h"

#define MODE_INPUT                        0
#define MODE_OUTPUT_SPEED_10MHZ           1
#define MODE_OUTPUT_SPEED_2MHZ            2
#define MODE_OUTPUT_SPEED_50MHZ           3

#define CONF_OUTPUT_GENERAL_PUSH_PULL     0
#define CONF_OUTPUT_GENERAL_OPEN_DRAIN    1
#define CONF_OUTPUT_ALTERNATE_PUSH_PULL   2
#define CONF_OUTPUT_ALTERNATE_OPEN_DRAIN  3

#define CONF_INPUT_ANALOG                 0
#define CONF_INPUT_FLOATING               1
#define CONF_INPUT_PULL_UP_DOWN           2
#define CONF_INPUT_RESERVED               3

// 初始化
void USART_Init(void)
{
    // 1. 配置时钟
    RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
    RCC->APB2ENR |= RCC_APB2ENR_USART1EN;

    // 2. GPIO工作模式
    // PA9: 复用推挽输出，CNF-10，MODE-11
    // PA10: 浮空输入，CNF-01，MODE-00
    GPIOA->CRH &= ~(0xF << (1 * 4));
    GPIOA->CRH |= MODE_OUTPUT_SPEED_50MHZ         << (1 * 4);
    GPIOA->CRH |= CONF_OUTPUT_ALTERNATE_PUSH_PULL << (1 * 4 + 2);

    GPIOA->CRH &= ~(0xF << (2 * 4));
    GPIOA->CRH |= MODE_INPUT                      << (2 * 4);
    GPIOA->CRH |= CONF_INPUT_FLOATING             << (2 * 4 + 2);

    // 3. 串口配置
    // 3.1 波特率设置: 115200
    USART1->BRR = 0x271;

    // 3.2 收发使能及模块使能
    USART1->CR1 |= (USART_CR1_UE | USART_CR1_TE | USART_CR1_RE);

    // 3.3 其它配置，设置数据帧格式
    USART1->CR1 &= ~USART_CR1_M;
    USART1->CR1 &= ~USART_CR1_PCE;
    USART1->CR2 &= ~USART_CR2_STOP;

    // 3.4 开启中断使能
    USART1->CR1 |= USART_CR1_IDLEIE;
    USART1->CR1 |= USART_CR1_RXNEIE;

    // 4. NVIC配置
    NVIC_SetPriorityGrouping(3);
    NVIC_SetPriority(USART1_IRQn, 3);
    NVIC_EnableIRQ(USART1_IRQn);
}

// 发送一个字符
void USART_SendChar(uint8_t ch)
{
    // 判断SR里TXE是否为1
    while ((USART1->SR & USART_SR_TXE) == 0)
    {
    }

    // 向DR写入新的要发送的数据
    USART1->DR = ch;
}

// 发送一个字符串
void USART_SendString(uint8_t *str, uint8_t size)
{
    for (uint8_t i = 0; i < size; i++)
    {
        USART_SendChar(str[i]);
    }
}

// 中断服务程序
void USART1_IRQHandler(void)
{
    // 首先判断中断类型
    if (USART1->SR & USART_SR_RXNE)
    {
        // 接收完成一个字符
        buffer[size] = USART1->DR;
        size++;
    }
    else if (USART1->SR & USART_SR_IDLE)            // 字符串整体接收完成
    {
        // 清除IDLE标志位
        USART1->DR;

        receive_one_frame_flag = 1;
    }
}
